// $URL: http://mingw-lib.googlecode.com/svn/trunk/working/avcodec_to_widget_8/widget.cpp $
// $Rev: 298 $
// $Author: akio.miyoshi $
// $Date:: 2010-07-28 14:12:27 +0000#$

#include "widget.h"
#include "ui_widget.h"

#include "stable.h"

#include "t_my_av_utils.h"
#include "t_box_player.h"
#include "t_box_input.h"
#include "t_box_input_file.h"
#include "t_box_input_http.h"
////#include "t_box_input_url.h"
#include "t_box_input_youtube.h"

#include "nam.h"
#include "t_httpbackend.h"

#include "t_youtube.h"

static void my_av_log_callback(void* ptr, int level, const char* fmt, va_list vl);

void T_ScreenWidget::paintEvent(QPaintEvent *e)
{
    QPainter v_p(this);
    //m_widget->m_video_surface->paint(&v_p, QRectF(0, 0, 1, 1));
    int v_font_size = 16;
    QFont v_f("MS UI Gothic", v_font_size, QFont::Bold);
    //QFontInfo fi(v_f);
    //qDebug() << fi.fixedPitch();
    v_f.setFixedPitch(true);
    v_p.setFont(v_f);
    //m_display_mutex.lock();
    v_p.drawImage(QPoint(0, 0), m_image);
    //m_display_mutex.unlock();
#if 0x0
    QTime v_elapsed(0, 0, 0, 0);
    v_elapsed = v_elapsed.addMSecs(m_time_line.elapsed());
    QString v_msg = QString("E=%1 F=%2 D=%3")
                    .arg(v_elapsed.toString("hh:mm:ss[zzz]"))
                    .arg(frameNo)
                    .arg(m_drop_count)
                    ;
    //[Time etc]
    int v_diff = 1;
    int v_x_off = 2;
    int v_y_off = this->height() - 4;
    v_p.setPen(Qt::white);
    v_p.drawText(QPoint(v_x_off+v_diff, v_y_off+v_diff), v_msg);
    v_p.setPen(Qt::black);
    v_p.drawText(QPoint(v_x_off, v_y_off), v_msg);
    //[FPS]
    v_x_off = 2;
    v_y_off = v_font_size + 4;
    //v_msg = QString("%1 fps").arg(m_fps);
    //v_msg = QString("%1 fps : %2 fps").arg(m_fps).arg(m_fps2.value());
    v_msg = QString("%1 fps : %2 fps")
            .arg(m_fps1.value(), 6, 'f', 2, ' ')
            .arg(m_fps2.value(), 6, 'f', 2, ' ');
    v_p.setPen(Qt::white);
    v_p.drawText(QPoint(v_x_off+v_diff, v_y_off+v_diff), v_msg);
    v_p.setPen(Qt::black);
    v_p.drawText(QPoint(v_x_off, v_y_off), v_msg);
#endif
}

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , m_media_input(0)
    , m_player_ctx(0)
    , m_packet_thread(0)
    , m_fps1(1000)
    , m_fps2(1000)
    , m_fps_download(500)
{
    ui->setupUi(this);

    screenWidget = new T_ScreenWidget(this);
    screenWidget->setObjectName(QString::fromUtf8("screenWidget"));
    ui->verticalLayout->addWidget(screenWidget);

    av_register_all();
    av_log_set_callback(my_av_log_callback);

    //m_media_input = get_input_file("C:/Soraya_-_Self_Control.mp4");
    //m_media_input = get_input_file("C:/6tQzHr5mgbc_34.flv");
    //m_media_input = get_input_file("C:/temp.flv");
    //m_media_input = get_input_youtube("hKL_EFvOLD8"); //「涙サプライズ＝vを踊ってみ...
    //m_media_input = get_input_youtube("_zjqJSCC4t4"); //Dan Balan Chica Bomb
    //m_media_input = get_input_youtube("Ywu1DeqXTg4"); //Iron Man vs Bruce Lee
    //m_media_input = get_input_youtube("mVEG793G3N4"); //Lady Gaga - Eh, Eh (Nothing Else I Can Say)

    //m_media_input = get_input_youtube2("qqIIW7nxBgc", 34); //Madonna - La Isla Bonita (Video)



    //m_media_input = get_input_youtube2("TqYen5ybVrg", 34); //Soraya - Self Control
    //m_media_input = get_input_youtube2("TqYen5ybVrg", 18); //Soraya - Self Control

    //m_media_input = get_input_youtube2("IwljUPa05Yo", 18); //Tommy february6 - Bloomin'!

    //m_media_input = get_input_youtube2("_QTDsV1d8rA", 18); //可憐Girl's - Over The Future / HD動画
    //m_media_input = get_input_youtube2("_QTDsV1d8rA", 35); //可憐Girl's - Over The Future / HD動画

    //m_media_input = get_input_youtube2("qD3_W7UFXho", 34); //[HD] 3DMark06 Demo 1+2 [Core i7 920] [GeForce 9800 GT/GE]
    //m_media_input = get_input_youtube2("qD3_W7UFXho", 18); //[HD] 3DMark06 Demo 1+2 [Core i7 920] [GeForce 9800 GT/GE]

    //m_media_input = get_input_youtube2("I_Ir0HrnxoM", 34); //れっと　みー　どりーむ　ライブver
    //m_media_input = get_input_youtube2("I_Ir0HrnxoM", 18); //れっと　みー　どりーむ　ライブver


    //m_media_input = get_input_youtube2("suKGwnNEwRM", 34); //m2m mirror mirror official music video
    //m_media_input = get_input_youtube2("suKGwnNEwRM", 18); //m2m mirror mirror official music video
    //m_media_input = get_input_youtube2("hepnvaB9F4Y", 18); //[HD] 3DMark06 Demo 3+4 [Core i7 920] [GeForce 9800 GT/GE]
    //m_media_input = get_input_youtube2("Z07Bzw6CRZg", 34); //恋の呪文はスキトキメキトキス　伊藤さやか
    //m_media_input = get_input_youtube2("Z07Bzw6CRZg", 18); //恋の呪文はスキトキメキトキス　伊藤さやか
    //m_media_input = get_input_youtube2("2YRmr4AusRg", 34); //SWEET BLACK feat. MAKI GOTO / Lady-Rise
    //m_media_input = get_input_youtube2("2YRmr4AusRg", 18); //SWEET BLACK feat. MAKI GOTO / Lady-Rise

    //m_media_input = get_input_youtube2("_QTDsV1d8rA", 34); //可憐Girl's - Over The Future / HD動画
    //m_media_input = get_input_youtube2("IwljUPa05Yo", 34); //Tommy february6 - Bloomin'!

    //m_media_input = get_input_youtube2("X78KXfzUf_c", 34); //森下悠里--キューティーハニー&タッチ
    //m_media_input = get_input_youtube2("X78KXfzUf_c", 18); //森下悠里--キューティーハニー&タッチ

    //m_media_input = get_input_youtube2("_QTDsV1d8rA", 22); //可憐Girl's - Over The Future / HD動画
    //m_media_input = get_input_youtube2("qD3_W7UFXho", 22); //[HD] 3DMark06 Demo 1+2 [Core i7 920] [GeForce 9800 GT/GE]
    //m_media_input = get_input_youtube2("hepnvaB9F4Y", 22); //[HD] 3DMark06 Demo 3+4 [Core i7 920] [GeForce 9800 GT/GE]
    //m_media_input = get_input_youtube2("-1KyXEsrnXI", 22); //モーニング娘。- 37 - ペッパ-警部 (Close-up Ver) [道重, 田中, ジュンジュン] HD
    //m_media_input = get_input_youtube2("Rm-0f_YafbY", 22); //HD AKB48 会いたかった

    //[HQ]
    //m_media_input = get_input_youtube2("seIp4AIK2bQ", 35); //[HQ] ƒ(x)- Chocolate Love MV Ver. 2

    //[HD 1080p]
    //m_media_input = get_input_youtube2("ebrmqcuAqgU", 22); //[HD+MP4] 090718 Girls' Generation (SNSD) - Tell Me Your Wish (Genie) On Music Core
    //m_media_input = get_input_youtube2("ebrmqcuAqgU", 37); //[HD+MP4] 090718 Girls' Generation (SNSD) - Tell Me Your Wish (Genie) On Music Core
    //m_media_input = get_input_youtube2("sOrbHOueDn4", 22); //パトレイバー op
    //m_media_input = get_input_youtube2("sOrbHOueDn4", 37); //パトレイバー op
    //m_media_input = get_input_youtube2("sOrbHOueDn4", 18); //パトレイバー op
    //m_media_input = get_input_youtube2("XLGA6FGKMfs", 18); //Orange Caramel (오렌지캬라멜) - Magic Girl 마법소녀 [HD]
    //m_media_input = get_input_youtube2("XLGA6FGKMfs", 22); //Orange Caramel (오렌지캬라멜) - Magic Girl 마법소녀 [HD]
    //m_media_input = get_input_youtube2("XLGA6FGKMfs", 37); //Orange Caramel (오렌지캬라멜) - Magic Girl 마법소녀 [HD]


    //[HD 720p]
    //m_media_input = get_input_youtube2("aWEhonJ2xvg", 18); //Crysis 2 - Nanosuit 2 trailer
    //m_media_input = get_input_youtube2("aWEhonJ2xvg", 22); //Crysis 2 - Nanosuit 2 trailer
    //m_media_input = get_input_youtube2("6dwvE0SJOSo", 22); //【PV】【HD】ジンギスカン Dance Shot Ver. 高画質 Berryz工房
    //m_media_input = get_input_youtube2("6dwvE0SJOSo", 18); //【PV】【HD】ジンギスカン Dance Shot Ver. 高画質 Berryz工房
    //m_media_input = get_input_youtube2("AZFYSO59icI", 18); //ジンギスカン Single 16 Berryz工房 HD版
    //m_media_input = get_input_youtube2("AZFYSO59icI", 22); //ジンギスカン Single 16 Berryz工房 HD版
    //m_media_input = get_input_youtube2("AZFYSO59icI", 34); //ジンギスカン Single 16 Berryz工房 HD版



    //m_media_input = get_input_youtube2("cZGwE2pL22U", 34); //残酷な天使のテーゼ／滝沢乃南
    //m_media_input = get_input_youtube2("8ySFADEvdBM", 34); //Robo Kiss-ロボキッス[Live:Hello!Project2008Winter]Sayumi and Risako
    //m_media_input = get_input_youtube2("8ySFADEvdBM", 18); //Robo Kiss-ロボキッス[Live:Hello!Project2008Winter]Sayumi and Risako
    //m_media_input = get_input_youtube2("5QxDuoCunF8", 34); //Berryz工房 - 16 - ジンギスカン (菅谷梨沙子 Ver)
    m_media_input = get_input_youtube2("5QxDuoCunF8", 18); //Berryz工房 - 16 - ジンギスカン (菅谷梨沙子 Ver)



    //m_media_input->setDebugMode(true);




    if(!m_media_input)
    {
        qDebug() << "(!m_media_input)";
        return;
    }

    if(!m_media_input->isValid())
    {
        qDebug() << "(!m_media_input->isValid())";
        return;
    }

    AVFormatContext	*v_format_ctx = m_media_input->getAVFormatContext();
    if(v_format_ctx==NULL)
    {
        qDebug() << "(v_format_ctx==NULL)";
        return;
    }

#if 0x0
    m_player_ctx = m_media_input->getPlayerContext();
    if(!m_player_ctx)
    {
        qDebug() << "Can't open video";
        return;
    }
#else
    m_player_ctx = new T_Box_Player_Context(m_media_input, v_format_ctx);
#endif

    if(!m_player_ctx->m_audio.isValid)
    {
        qDebug() << "(!m_player_ctx->m_audio.isValid)";
        return;
    }

    if(!m_player_ctx->m_video.isValid)
    {
        qDebug() << "(!m_player_ctx->m_video.isValid)";
        return;
    }

#if 0x0
    qDebug() << "[before av_find_stream_info()]";
    if(av_find_stream_info(m_format_ctx)<0)
    {
        qDebug() << "Can't find stream info";
    }
#endif

    qDebug() << "[before dump_format()]";
    dump_format(m_player_ctx->m_format_ctx, 0, m_media_name.toLocal8Bit().constData(), false);
    qDebug() << "[after dump_format()]";


    QString v_print;
    //[Format Name]
    qDebug() << v_print.sprintf(
            "formatCtx->iformat->long_name=%s (->name=%s)"
            , m_player_ctx->m_format_ctx->iformat->long_name
            , m_player_ctx->m_format_ctx->iformat->name
            );
    //[Meta Data]
    AVMetadata *m = m_player_ctx->m_format_ctx->metadata;
    if(m && !(m->count == 1 && av_metadata_get(m, "language", NULL, 0))){
        AVMetadataTag *tag=NULL;
        av_log(NULL, AV_LOG_INFO, "%sMetadata:\n", "  ");
        while((tag=av_metadata_get(m, "", tag, AV_METADATA_IGNORE_SUFFIX))) {
            if(strcmp("language", tag->key))
                av_log(NULL, AV_LOG_INFO, "%s  %-16s: %s\n", "  ", tag->key, tag->value);
        }
    }

    //[nb_programs]
    qDebug() << QString("formatCtx->nb_programs=%1").arg(m_player_ctx->m_format_ctx->nb_programs);

    //[nb_chapters]
    qDebug() << QString("formatCtx->nb_chapters=%1").arg(m_player_ctx->m_format_ctx->nb_chapters);

    //[nb_streams]
    qDebug() << QString("formatCtx->nb_streams=%1").arg(m_player_ctx->m_format_ctx->nb_streams);

    for(int i=0;i<(int)m_player_ctx->m_format_ctx->nb_streams;i++)
    {
        my_dump_stream_format(m_player_ctx->m_format_ctx, i, 0, false);
        AVStream *st = m_player_ctx->m_format_ctx->streams[i];
        int g = av_gcd(st->time_base.num, st->time_base.den);
        av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d\n", st->codec_info_nb_frames, st->time_base.num/g, st->time_base.den/g);
        char buf[256];
        my_avcodec_string(buf, sizeof(buf), st->codec, false);
        qDebug() << buf;
    }

    //[Video/SDL]
    //SDL_putenv("SDL_VIDEODRIVER=windib");
    //SDL_putenv("SDL_VIDEODRIVER=directx");
    //SDL_putenv(QString("SDL_WINDOWID=%1").arg((quint32)this->screenWidget->winId()).toLatin1().constData());
    SDL_Init(SDL_INIT_VIDEO);
    window_id = SDL_CreateWindowFrom((void *)this->screenWidget->winId());
    Uint32 flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
    screenWidget->screen = SDL_SetVideoModeEx(
            screenWidget->width(), //640,
            screenWidget->height(), //360,
            0,
            flags,
            window_id);
    if( ! screenWidget->screen )
    {
        qDebug() << "( ! screenWidget->screen )";
        return;
    }

    char namebuf[256+1];
    qDebug() << "[SDL_VideoDriverName()]" << SDL_VideoDriverName(namebuf, sizeof(namebuf)-1);

    qDebug() << "[audioCodec->long_name]" << m_player_ctx->m_audio.codec->long_name;
    qDebug() << "[audioCtx->sample_fmt]" << m_player_ctx->m_audio.codec_ctx->sample_fmt;
    qDebug() << "[audioCtx->sample_rate]" << m_player_ctx->m_audio.codec_ctx->sample_rate;


    m_player_ctx->v_prog = new T_VideoProgressBar(this);
    //m_player_ctx->v_prog = new T_VideoProgressBar2(this);
    m_player_ctx->v_prog->setMaximum(m_player_ctx->duration());
    connect(m_player_ctx->v_prog, SIGNAL(clicked(int)), SLOT(on_progress_clicked(int)));
    ui->verticalLayout->addWidget(m_player_ctx->v_prog);

    m_player_ctx->v_prog2 = new T_BufferProgressBar(this);
    m_player_ctx->v_prog2->setMaximum(m_player_ctx->duration());
    ui->verticalLayout->addWidget(m_player_ctx->v_prog2);


    //[Audio/Qt]
    m_format.setFrequency(m_player_ctx->m_audio.codec_ctx->sample_rate);
    m_format.setChannels(m_player_ctx->m_audio.codec_ctx->request_channels);
    m_format.setSampleSize(16);
    m_format.setCodec("audio/pcm");
    m_format.setByteOrder(QAudioFormat::LittleEndian);
    m_format.setSampleType(QAudioFormat::SignedInt);

    m_device = QAudioDeviceInfo::defaultOutputDevice();
    if(!m_device.isFormatSupported(m_format))
    {
        qDebug() << "(!m_device.isFormatSupported(m_format))";
        return;
    }

    //double v_time_base = 1.0 * v_stream->time_base.num / v_stream->time_base.den;
    qDebug()<< "[formatCtx->duration]"
            << m_player_ctx->m_format_ctx->duration
            << QString("[%1 sec]").arg(m_player_ctx->m_format_ctx->duration / AV_TIME_BASE);
    qDebug()<< "[m_video_stream->start_time]"
            << m_player_ctx->m_video.stream->start_time
            ;
    qDebug()<< "[videoCtx->time_base]"
            << m_player_ctx->m_video.codec_ctx->time_base.num
            << m_player_ctx->m_video.codec_ctx->time_base.den
            << 1.0 * m_player_ctx->m_video.codec_ctx->time_base.num / m_player_ctx->m_video.codec_ctx->time_base.den
            ;
    qDebug()<< "[audioCtx->time_base]"
            << m_player_ctx->m_audio.codec_ctx->time_base.num
            << m_player_ctx->m_audio.codec_ctx->time_base.den
            << 1.0 * m_player_ctx->m_audio.codec_ctx->time_base.num / m_player_ctx->m_audio.codec_ctx->time_base.den
            ;
    qDebug()<< "[m_video_stream->avg_frame_rate]"
            << m_player_ctx->m_video.stream->avg_frame_rate.num
            << m_player_ctx->m_video.stream->avg_frame_rate.den
            << 1.0 * m_player_ctx->m_video.stream->avg_frame_rate.num / m_player_ctx->m_video.stream->avg_frame_rate.den
            ;
    qDebug()<< "[m_video_stream->r_frame_rate]"
            << m_player_ctx->m_video.stream->r_frame_rate.num
            << m_player_ctx->m_video.stream->r_frame_rate.den
            << 1.0 * m_player_ctx->m_video.stream->r_frame_rate.num / m_player_ctx->m_video.stream->r_frame_rate.den
            ;
    qDebug()<< "[m_video_stream->time_base]"
            << m_player_ctx->m_video.stream->time_base.num
            << m_player_ctx->m_video.stream->time_base.den
            << 1.0 * m_player_ctx->m_video.stream->time_base.num / m_player_ctx->m_video.stream->time_base.den
            ;
    qDebug()<< "[m_audio.stream->time_base]"
            << m_player_ctx->m_audio.stream->time_base.num
            << m_player_ctx->m_audio.stream->time_base.den
            << 1.0 * m_player_ctx->m_audio.stream->time_base.num / m_player_ctx->m_audio.stream->time_base.den
            ;

    //frameNo=0;
    m_drop_count = 0;


    //this->setWindowTitle(filename);
    this->setWindowTitle(QString("%1 %2")
                         .arg(m_player_ctx->durationToString("hh:mm:ss[zzz]"))
                         .arg(m_media_name)
                         );


#if 0x1
    if(m_player_ctx->m_video.codec_ctx->width >= 1280
       ||
       m_player_ctx->m_video.codec_ctx->height >= 720
       )
    {
        this->screenWidget->setScreenSize(
                QSize(m_player_ctx->m_video.codec_ctx->width/2,
                      m_player_ctx->m_video.codec_ctx->height/2)
                );
    }
    else
#endif
    {
        this->screenWidget->setScreenSize(
                QSize(m_player_ctx->m_video.codec_ctx->width,
                      m_player_ctx->m_video.codec_ctx->height)
                );
    }
    this->screenWidget->adjustSize();

    if(false)
    {
        QMutexLocker(m_player_ctx->m_audio.packet_queue.mutex());
        QMutexLocker(m_player_ctx->m_video.packet_queue.mutex());
        while(m_player_ctx->enqueuePacket())
        {
            //qDebug() << m_player_ctx->m_audio.packet_queue.size() << m_player_ctx->m_video.packet_queue.size();

        }
    }

    //qDebug() << "[m_video.packet_queue.size()]" << m_player_ctx->m_video.packet_queue.size();
    //qDebug() << "[m_audio.packet_queue.size()]" << m_player_ctx->m_audio.packet_queue.size();

    m_packet_thread = new T_Packet_Thread(m_player_ctx, this);
    m_packet_thread->start();

    connect(&m_video_timer, SIGNAL(timeout()), SLOT(onTimerTimeout()));
    //m_video_timer.start(1);
    m_video_timer.start(5);
    //m_video_timer.start(10);
    //m_video_timer.start(30);

    m_player_ctx->m_generator = new T_Generator(m_player_ctx, 0);
    QAudioOutput *v_audioOutput = new QAudioOutput(m_device, m_format, this);
    m_player_ctx->setAudioOutput(v_audioOutput);
    v_audioOutput->start(m_player_ctx->m_generator);
    qDebug() << "[m_audioOutput->error()]" << v_audioOutput->error();
}

void T_Packet_Thread::run()
{
    //qApp->setStartDragDistance(10);
    //qApp->setStartDragTime(100);
    T_AV_Packet_Queue *v_audio_qu = &m_player_ctx->m_audio.packet_queue;
    T_AV_Packet_Queue *v_video_qu = &m_player_ctx->m_video.packet_queue;
    for(;;)
    {
        //qDebug() << "[m_closed]" << m_closed;
        if(m_closed) break;
        {
            if(m_player_ctx->v_prog->seekRequest() && m_player_ctx->seekTimer()->elapsed()>100)
            //if(m_player_ctx->v_prog->seekRequest())
            {
                int v_seek_target = m_player_ctx->v_prog->seekTarget();
                //qDebug() << "[T_Packet_Thread::run()]" << m_player_ctx->seekTimer()->elapsed() << v_seek_target;
                qDebug() << "[T_Packet_Thread::run(v_seek_target|m_max_timing)]: ()" << v_seek_target << m_max_timing;
                if(v_seek_target>m_max_timing)
                {
                    v_seek_target = m_max_timing;
                }
                m_player_ctx->seek(v_seek_target);
                m_player_ctx->seekTimer()->reset();
                m_player_ctx->v_prog->seekRequest(false);
                QMetaObject::invokeMethod(m_widget, "onTimerTimeout", Qt::QueuedConnection);
            }
            if(m_player_ctx->needBuffering())
            {
                //go to reade packet.
            }
            else if(m_player_ctx->seekTimer()->elapsed()<700)
            {
                //msleep(1); //N.G.: Don't do this.
#if 0x1
                //qDebug() << "(A1)";
                ////m_player_ctx->v_prog->seekWaitMutex()->lock();
                //qDebug() << "(A2)";
                m_player_ctx->v_prog->seekWaitCondition()->wait(1);
                //qDebug() << "(A3)";
                ////m_player_ctx->v_prog->seekWaitMutex()->unlock();
                //qDebug() << "(A4)";
#endif
                continue;
            }
            T_AV_Packet *v_av_packet = m_player_ctx->enqueuePacket();
            if(m_max_timing == LONG_LONG_MAX)
            {
                //
            }
            else if(!v_av_packet)
            {
                m_max_timing = LONG_LONG_MAX;
                qDebug() << "[m_max_timing = LONG_LONG_MAX]" << m_max_timing;
            }
            else
            {
                ////if(v_av_packet->m_frame_no % 1000 == 0) qDebug() << "READ PACKET:" << v_av_packet->m_frame_no;
                int v_hedge = T_Box_Player_Context::PLAYER_HEAD_HEADGE;
                if(v_av_packet && v_av_packet->timing()>(m_max_timing+v_hedge))
                {
                    m_max_timing = v_av_packet->timing()-v_hedge;
                    //qDebug() << "[m_max_timing = v_av_packet->timing()-v_hedge]" << m_max_timing;
                }
            }
            if(!v_av_packet)
            {
                //qDebug() << "(1)";
                ////m_player_ctx->v_prog->seekWaitMutex()->lock();
                //qDebug() << "(2)";
                //m_player_ctx->v_prog->seekWaitCondition()->wait(m_player_ctx->v_prog->seekWaitMutex(), 1000);
                m_player_ctx->v_prog->seekWaitCondition()->wait(1);
                //qDebug() << "(3)";
                ////m_player_ctx->v_prog->seekWaitMutex()->unlock();
                //qDebug() << "(4)";
                //msleep(50);
                //msleep(1);
            }
        }
    }
}

Widget::~Widget()
{
    qDebug() << "[Widget::~Widget()]";
    //if(m_audioOutput) m_audioOutput->stop();
    qDebug() << "[Widget::~Widget(1)]";
    if(m_packet_thread)
    {
        qDebug() << "[Widget::~Widget(2)]";
        //m_packet_thread->terminate();
        m_packet_thread->close();
        qDebug() << "[Widget::~Widget(2.1)]";
        //m_packet_thread->wait();
        wait_thread(m_packet_thread);
        qDebug() << "[Widget::~Widget(3)]";
    }
    m_video_timer.stop();
    qDebug() << "[Widget::~Widget(4)]";

    delete m_player_ctx;
    qDebug() << "[Widget::~Widget(4.1)]";
    delete m_media_input;
    qDebug() << "[Widget::~Widget(4.2)]";
    delete ui;
    qDebug() << "[Widget::~Widget(5)]";
}

void Widget::onTimerTimeout()
{
    QMutexLocker v_lock(&m_frame_mutex);
    Q_UNUSED(v_lock);
    //[seek lock]
    QReadWriteLock *v_seek_mutex = m_player_ctx->seekMutex();
    if(!v_seek_mutex->tryLockForRead(100))
    {
        qDebug() << "[Widget::onTimerTimeout()]: (!v_queue_lock->tryLock(100))";
        return;
    }
    QReadLocker v_seek_locker(v_seek_mutex);
    Q_UNUSED(v_seek_locker);
    v_seek_mutex->unlock();
    //
    qint64 v_audio_elapsed = m_player_ctx->m_generator->elapsed();
    if(v_audio_elapsed >= 0)
    {
        qint64 v_video_elapsed = m_player_ctx->m_stopwatch.elapsed();
        qint64 v_elapsed_diff = v_audio_elapsed - v_video_elapsed;
        m_player_ctx->m_stopwatch.setElapsed(v_video_elapsed + (v_elapsed_diff/2));
    }
    show_status();
    //qDebug() << "[Widget::onTimerTimeout()]";
#if 0x1
    static bool s_first = true;
    qint64 v_target = s_first ?
                      T_Box_Player_Context::PLAYER_HEAD_HEADGE * 2 :
                      T_Box_Player_Context::PLAYER_HEAD_HEADGE * 3 ;
#if 0x0
    if(m_player_ctx->m_video.packet_queue.isComplete())
    {
        //
    }
    else if(m_player_ctx->m_video.packet_queue.size()>0 && m_player_ctx->m_video.packet_queue.head()->m_frame_no==0)
    {
        //
    }
    else
#endif
    if(!m_boost_timer.isActive())
    {
        if(m_player_ctx->needBuffering())
#if 0x0
        if(//m_player_ctx->m_audio.packet_queue.duration()<v_target
           //||
           m_player_ctx->m_video.packet_queue.duration()<v_target
           )
#endif
        {
            suspend_time(QString::number(__LINE__)+"[A]");
            m_boost_timer.reset();
            m_boost_timer.start();
            return;
        }
        s_first = false;
    }
    else
    {
        if(!m_player_ctx->needBuffering())
#if 0x0
        if(//m_boost_timer.elapsed()>=3000
                //&&
                //m_player_ctx->m_audio.packet_queue.duration()>=v_target
                //&&
                m_player_ctx->m_video.packet_queue.duration()>=v_target
                )
#endif
        {
            m_boost_timer.stop();
        }
        else
        {
            suspend_time(QString::number(__LINE__)+"[B]");
            return;
        }
        s_first = false;
    }
#endif
    if(m_player_ctx->m_video.packet_queue.size()==0)
    {
        // end of queue
        //qDebug() << "Widget::onTimerTimeout(1)";
        //qDebug() << "[stop(3)]" << m_player_ctx->m_stopwatch.toString("hh:mm:ss[zzz]");
        if(m_player_ctx->m_video.packet_queue.isComplete())
        {
            resume_time();
        }
        else
        {
            suspend_time(QString::number(__LINE__)+"[C]");
        }
        return;
    }
    resume_time();
    int v_elapsed = m_player_ctx->m_stopwatch.elapsed();
    //qDebug() << "[v_elapsed]" << v_elapsed << m_player_ctx->m_time_line.isActive();

    T_AV_Packet *v_head_packet = m_player_ctx->m_video.packet_queue.head();
    if(v_head_packet->isNull())
    {
        if(v_elapsed < m_player_ctx->duration())
        {
            resume_time();
        }
        else
        {
            m_player_ctx->m_stopwatch.setElapsed(m_player_ctx->duration());
            suspend_time(QString::number(__LINE__)+"[D]");
        }
        return;
    }
    else if(v_elapsed < v_head_packet->timing())
    {
        // too early
        //qDebug() << "Widget::onTimerTimeout(2)" << v_elapsed << v_head_packet->timing();
        //resume_time();
        return;
    }
    for(;;)
    {
        //T_AV_Packet *v_last_packet = m_player_ctx->m_video.packet_queue.last();
        //Q_ASSERT(v_last_packet);
        T_AV_Packet *v_curr_packet = m_player_ctx->m_video.packet_queue.dequeue();
        {
            if(m_player_ctx->m_video.packet_queue.size()==0)
            {
                //there is no next frame
                // should render current frame
                //qDebug() << "(1)       [frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                m_fps1.add(1);
                m_fps2.add(1);
            }
            else
            {
                T_AV_Packet *v_next_packet = m_player_ctx->m_video.packet_queue.head();
                if(v_elapsed >= v_next_packet->timing())
                {
                    // should render next frame
                    //qDebug() << "=====>DROP[frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                    m_fps1.add(1);
                    m_drop_count++;
                    v_curr_packet->decodeVideo(NULL);
                    delete v_curr_packet;
                    v_curr_packet = NULL;
                    continue;
                }
                // should render current frame
                //qDebug() << "(2)       [frameNo]" << v_curr_packet->m_frame_no << "[packet.pts]" << v_curr_packet->timing();
                m_fps1.add(1);
                m_fps2.add(1);
            }
            if(true)
            {
                //QVideoFrame *v_qvf = v_curr_packet->decodeToQVideoFrame();
                //m_video_surface->present(*v_qvf);
                //QImage v_image = v_curr_packet->decodeToQImage(screenWidget->size(), 16);
                QImage v_image = v_curr_packet->decodeToQImage(screenWidget->size(), 32);
                if(!v_image.isNull())
                {
                    screenWidget->m_image = v_image;
                    screenWidget->update();
                }
            }
            else
            {
                int v_ovl_w = screenWidget->width();
                int v_ovl_h = screenWidget->height();
                v_ovl_w = (v_ovl_w / 2) * 2;
                v_ovl_h = (v_ovl_h / 2) * 2;
                if(
                        v_ovl_w != screenWidget->screen->w
                        ||
                        v_ovl_h != screenWidget->screen->h
                        )
                {
                    Uint32 flags = SDL_HWSURFACE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_RESIZABLE;
                    screenWidget->screen = SDL_SetVideoModeEx(
                            v_ovl_w,
                            v_ovl_h,
                            0,
                            flags,
                            window_id);
                }
                SDL_Overlay *ovl = SDL_CreateYUVOverlay(
                        v_ovl_w,
                        v_ovl_h,
                        SDL_YV12_OVERLAY,
                        screenWidget->screen);
                v_curr_packet->decodeVideo(ovl);
                SDL_Rect rect;
                rect.x = 0;
                rect.y = 0;
                rect.w = ovl->w;
                rect.h = ovl->h;
                SDL_DisplayYUVOverlay(ovl, &rect);
                SDL_FreeYUVOverlay(ovl);
            }
        }
        delete v_curr_packet;
        v_curr_packet = NULL;
        break;
    }
}

static void my_av_log_callback(void* ptr, int level, const char* fmt, va_list vl)
{
    static QByteArray s_buffer;
    QString v_print;
    v_print.vsprintf(fmt, vl);
    s_buffer.append(v_print.toLatin1());
    QBuffer v_buffer(&s_buffer);
    v_buffer.open(QIODevice::ReadOnly);
    if(v_buffer.canReadLine())
    {
        QString v_line = v_buffer.readLine();
        v_line.replace(QRegExp("(\\r|\\n)"), "");
        qDebug() << "[av_log]" << v_line;
        s_buffer = v_buffer.readAll();
    }
    v_buffer.close();
}

void Widget::suspend_time(const QString & a_suspend_pos)
{
    m_suspend_pos = a_suspend_pos;
    m_suspend_time = QDateTime::currentDateTime();
    m_player_ctx->m_stopwatch.stop();
}

void Widget::resume_time()
{
    m_player_ctx->m_stopwatch.start();
    if(m_player_ctx->m_audioOutput && m_player_ctx->m_audioOutput->state()==QAudio::IdleState)
    {
        m_player_ctx->m_audioOutput->stop();
        m_player_ctx->m_audioOutput->start(m_player_ctx->m_generator);
    }
}

void Widget::show_status()
{
    static int s_show_count = 0;
    static T_AutoRunStopWatch s_sw;
    s_show_count++;
    QMutexLocker(m_player_ctx->m_video.packet_queue.mutex());
    T_AV_Packet_Queue &v_packet_queue = m_player_ctx->m_video.packet_queue;
    if(v_packet_queue.size()==0) return;
    T_AV_Packet *v_curr_packet = v_packet_queue.head();
    T_AV_Packet *v_last_packet = v_packet_queue.last();
    ////if(v_curr_packet && v_last_packet)
    {
        m_fps_download.set(v_last_packet->m_frame_no);
        QString v_msg = QString("%1:%2[%3 x %4] DL:%5 %6%7(%8)")
                .arg(m_fps1.value(), 6, 'f', 2, ' ')
                .arg(m_fps2.value(), 6, 'f', 2, ' ')
                .arg(screenWidget->width())
                .arg(screenWidget->height())
                .arg(m_fps_download.value(), 6, 'f', 2, ' ')
                .arg(v_curr_packet->isNull())
                .arg(v_last_packet->isNull())
                .arg(v_last_packet->m_frame_no - v_curr_packet->m_frame_no)
                //.arg(m_boost_timer.isActive())
                ;
        this->ui->lineEdit->setText(v_msg);

        v_msg = QString("E=%1 F=%2/%3 D=%4")
                .arg(m_player_ctx->m_stopwatch.toString("hh:mm:ss[zzz]"))
                .arg(v_curr_packet->m_frame_no)
                .arg(v_last_packet->m_frame_no)
                .arg(m_drop_count)
                //.arg(m_suspend_time.toString("hh:mm:ss[zzz]"))
                //.arg(m_suspend_pos+":LN")
                ;
        this->ui->lineEdit_2->setText(v_msg);
    }

    //if(s_sw.elapsed()>=100)
    {
        //s_sw.reset();
        m_player_ctx->v_prog->setTiming(m_player_ctx->m_stopwatch.elapsed());
    }

    if(m_player_ctx->m_video.packet_queue.isComplete())
    {
        m_player_ctx->v_prog2->setBuffering(false);
        m_player_ctx->v_prog2->setValue(m_player_ctx->duration());
    }
    else if(v_last_packet->timing() > m_player_ctx->v_prog2->value())
    {
        Q_ASSERT(v_last_packet);
        m_player_ctx->v_prog2->setBuffering(m_boost_timer.isActive());
        int v_hedge = T_Box_Player_Context::PLAYER_HEAD_HEADGE;
        int v_timing = v_last_packet->timing() - v_hedge;
        if(v_timing<0) v_timing=0;
        m_player_ctx->v_prog2->setValue(v_timing);
    }
}

T_Box_Input *Widget::get_input_file(const QString &a_filename)
{
    Q_ASSERT(m_media_input==NULL);
    m_media_name = a_filename;
    //setWindowTitle(a_filename);
    return new T_Box_Input_File(a_filename);
}

T_Box_Input *Widget::get_input_youtube(const QString &a_video_id)
{
    Q_ASSERT(m_media_input==NULL);
    m_media_name = a_video_id;
    //setWindowTitle(a_video_id);
    T_Youtube v_youtube(&m_nam);
    QMap<int, QUrl> v_fmt_map;
    QString v_token = v_youtube.getVideoStreamMap(a_video_id, v_fmt_map);

    qDebug() << "[v_token]" << v_token;
    qDebug() << "[v_fmt_map]" << v_fmt_map;
    qDebug() << "[v_fmt_map.value(22)]" << v_fmt_map.value(22);
    qDebug() << "[v_fmt_map.value(34)]" << v_fmt_map.value(34);

    NetworkBatch v_nbat(&m_nam);

    //QVariant v_loc = v_fmt_map.value(34);

    int v_fmt = v_fmt_map.value(22).isValid() ? 22 : 34;
    m_media_name = a_video_id+":"+QString::number(v_fmt);
    QVariant v_loc = v_fmt_map.value(v_fmt);
    QNetworkReply *v_reply = NULL;
    while(v_loc.isValid())
    {
        delete v_reply;
        qDebug() << "[v_loc]" << v_loc;
        v_reply = v_nbat.head(v_loc.toUrl());
        v_loc = v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->header(QNetworkRequest::ContentTypeHeader);
        qDebug() << v_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug() << v_reply->header(QNetworkRequest::LocationHeader);
        foreach(QByteArray v_first, v_reply->rawHeaderList())
        {
            qDebug() << v_first << v_reply->rawHeader(v_first);
        }
    }

    QVariant v_cont_header = v_reply->header(QNetworkRequest::ContentTypeHeader);
    if(!v_cont_header.isValid())
    {
        qDebug() << "(!v_cont_header.isValid())";
        return NULL;
    }

    qDebug() << v_cont_header;

    if(!v_cont_header.toString().startsWith("video/"))
    {
        qDebug() << "(!v_cont_header.toString().startsWith(\"video/\"))";
        return NULL;
    }

    qDebug() << "ok";

#if 0x1
    QNetworkRequest v_video_request(v_reply->url());
    QNetworkReply *v_video_reply = m_nam.get(v_video_request);
    return new T_Box_Input_Http(v_video_reply);
#else
    int v_target_size = 500000;
    //int v_thread_count = 1;
    int v_thread_count = 8;
    //int v_thread_count = 16;
    //int v_thread_count = 32;
    //int v_thread_count = 64;
    //int v_thread_count = 128;

    QNetworkRequest v_request(v_reply->url());
    v_reply->deleteLater();;
    v_request.setHeader(
            QNetworkRequest::CookieHeader,
            qVariantFromValue(m_nam.cookieJar()->cookiesForUrl(v_reply->url())));
    return new T_Box_Input_Url(v_request, v_thread_count, v_target_size);
#endif
}

T_Box_Input *Widget::get_input_youtube2(const QString &a_video_id, int a_fmt)
{
    Q_ASSERT(m_media_input==NULL);
#if 0x0
    m_media_name = a_video_id+":"+QString::number(a_fmt);
    return new T_Box_Input_Youtube(a_video_id, a_fmt);
#endif
    m_media_name = a_video_id+":"+QString::number(a_fmt);
    T_Youtube v_youtube(&m_nam);
    QVariant v_loc = v_youtube.getVideoStreamUrl(a_video_id, a_fmt);
    NetworkBatch v_nbat(&m_nam);
    QNetworkReply *v_reply = NULL;
    while(v_loc.isValid())
    {
        delete v_reply;
        qDebug() << "[v_loc]" << v_loc;
        v_reply = v_nbat.head(v_loc.toUrl());
        v_loc = v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute);
        qDebug() << v_reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
        qDebug() << v_reply->header(QNetworkRequest::ContentTypeHeader);
        qDebug() << v_reply->header(QNetworkRequest::ContentLengthHeader);
        qDebug() << v_reply->header(QNetworkRequest::LocationHeader);
        foreach(QByteArray v_first, v_reply->rawHeaderList())
        {
            qDebug() << v_first << v_reply->rawHeader(v_first);
        }
    }

    QVariant v_cont_header = v_reply->header(QNetworkRequest::ContentTypeHeader);
    if(!v_cont_header.isValid())
    {
        qDebug() << "(!v_cont_header.isValid())";
        return NULL;
    }

    qDebug() << v_cont_header;

    if(!v_cont_header.toString().startsWith("video/"))
    {
        qDebug() << "(!v_cont_header.toString().startsWith(\"video/\"))";
        return NULL;
    }

    qDebug() << "ok";
    int v_target_size = 500000;
    //int v_thread_count = 1;
    int v_thread_count = 8;
    //int v_thread_count = 16;
    //int v_thread_count = 32;
    //int v_thread_count = 64;
    //int v_thread_count = 128;

    m_media_name = a_video_id+":"+QString::number(a_fmt)+"="+v_cont_header.toString()
                   +QString(" thread=%1x%2").arg(v_thread_count).arg(v_target_size);
#if 0x0
    QNetworkRequest v_request(v_reply->url());
    v_request.setHeader(
            QNetworkRequest::CookieHeader,
            qVariantFromValue(m_nam.cookieJar()->cookiesForUrl(v_reply->url())));
    return new T_Box_Input_Url(v_request, v_thread_count, v_target_size);
#else
    QNetworkRequest v_video_request(v_reply->url());
    QNetworkReply *v_video_reply = m_nam.get(v_video_request);
    return new T_Box_Input_Http(v_video_reply);
#endif
}

void Widget::on_pushButton_clicked()
{
    ////m_packet_thread->seek_value(0);
}

void Widget::on_pushButton_2_clicked()
{
    ////m_packet_thread->seek();
}

void Widget::on_progress_clicked(int value)
{
    ////m_packet_thread->seek_value(value);
}
